﻿//*****************************************************************************
// (C) Copyright 2010 Tiago Pascoal
// This code is licensed under the Microsoft Public License (Ms-PL) 
// [http://www.microsoft.com/opensource/licenses.mspx#Ms-PL].
//
// This code is part of the Work Item Visualization project (
// http://visualization.codeplex.com)
//
//*****************************************************************************

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using System.Diagnostics;
using Microsoft.TeamFoundation.WorkItemTracking.Controls;

namespace WorkItemVisualization.Controls.QueryEditorControl
{
    public partial class ColumnOptionsForm : Form
    {
        private WorkItemStore _workItemStore;

        public string TeamProject { get; set; }

        /// <summary>
        /// Contains the list of fields that are available to be selected at a given time based on the project
        /// and the work item type. This field is updated everytime a project or a work item type is selected
        /// on the filters
        /// </summary>
        private FieldDefinitionList _selectableFields;
        /// <summary>
        /// Flag that indicates that we are in the middle of updating fields.
        /// </summary>
        private volatile bool _updatingFields = false;
        private ImageList _fieldChooserImageList;

        private List<ControlTracker> _trackedControls;
        private int _originalClientSizeWidth;


        #region constants
        //constants used in the UI. These are good candidates to be placed in resources if we need to support multi languages
        private const string AscendingOrder = "Ascending";
        private const string DescendingOrder = "Descending";
        private const string DefaultSortOrder = AscendingOrder; //The default sort order for new selected fields

        private const string AllWorkItemsTypes = "All Work Items Types";
        #endregion

        /// <summary>
        /// Gets team project that is selected on the filter.
        /// 
        /// If all projects is selected then it returns null
        /// 
        /// Since both filters are kept in sync we just fetch the selected item from the fields
        /// tab (even when the sort tab is active)
        /// </summary>
        public string SelectedTeamProject
        {
            get
            {
                if (projectComboBox.SelectedIndex == 0) return null;

                return (string)projectComboBox.SelectedItem;
            }
        }

        /// <summary>
        /// Predicate that indicates if a team project is selected on the team project filter
        /// 
        /// Since both filters are kept in sync we just fetch the selected item from the fields
        /// tab (even when the sort tab is active)
        /// </summary>
        public bool IsTeamProjectSelected
        {
            get
            {
                return projectComboBox.SelectedIndex > 0;
            }
        }

        /// <summary>
        /// Returns the work item type (name) that is selected on the filter
        /// 
        /// If the work item type filter is disable it throws an exception.
        /// 
        /// If all work item types is selected then we return null 
        /// </summary>
        public string SelectedWorkItemType
        {
            get
            {
                if (workItemTypeComboBox.Enabled == false) throw new Exception("Work Item Type is disabled");

                if (workItemTypeComboBox.SelectedIndex == 0) return null;

                return (string)workItemTypeComboBox.SelectedItem;
            }
        }

        /// <summary>
        /// Gets the value that is currently inputted in the width text box.
        /// 
        /// If the textbox is empty 0 (zero) is returned
        /// </summary>
        public string ColumnWidth
        {
            get
            {
                if (string.IsNullOrEmpty(maskedWitdhTextBox.Text))
                    return "0";

                return maskedWitdhTextBox.Text;
            }
        }

        /// <summary>
        /// Contains the list of fields that will be projected in the query.
        /// </summary>
        public DisplayFieldCollection DisplayFields { get; set; }

        /// <summary>
        /// Containst the list of fields that will be used to sort the query results ln
        /// </summary>
        public SortFieldList SortFields { get; set; }

        #region ctor
        private ColumnOptionsForm()
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="store"></param>
        /// <param name="displayFields"></param>
        /// <param name="sortFields"></param>
        public ColumnOptionsForm(WorkItemStore store, DisplayFieldCollection displayFields, SortFieldList sortFields)
        {
            InitializeComponent();
            InitializeFieldChooserImages();
            InitializeButtonImages();

            this._workItemStore = store;
            this.DisplayFields = displayFields;
            this.SortFields = sortFields;

            UpdateMoveUpDownButtonsState(selectedFieldsListView, moveUpButton, moveDownButton);
            UpdateMoveUpDownButtonsState(sortFieldsListView, moveSortUpButton, moveSortDownButton);
            UpdateSortButtonsState(sortFieldsListView);

            availableFieldsListView.SizeChanged += new EventHandler(availableFieldsListView_SizeChanged);
            sortAvailableFieldsListView.SizeChanged += new EventHandler(availableFieldsListView_SizeChanged);


            // Keep size of the original width so we can react appropriately when the window is resized
            _originalClientSizeWidth = this.ClientSize.Width;

            InitializeTrackedControls();
        }        
        
        #endregion

        /// <summary>
        /// Initializes the controls that are going to be tracked. 
        /// 
        /// These are the controls that we want to resize and/or move when the window is resized.
        /// 
        /// Different controls will have different operations performed.
        /// 
        /// The buttons in the middle will be moved and be kept at the same place (in a relative position
        /// to the initial size and the delta of window size change).
        /// 
        /// The right list views will only be resized and the left list views will be moved and resized.
        /// 
        /// Other will be resized and other will be moved and resized
        /// </summary>
        private void InitializeTrackedControls()
        {
            _trackedControls = new List<ControlTracker>();

            _trackedControls.Add(new ControlTracker(selectedFieldsListView, TrackerOperation.Move | TrackerOperation.Resize));
            _trackedControls.Add(new ControlTracker(availableFieldsListView, TrackerOperation.Resize));
            _trackedControls.Add(new ControlTracker(moveRightButton, TrackerOperation.Move));
            _trackedControls.Add(new ControlTracker(moveLeftButton, TrackerOperation.Move));
            _trackedControls.Add(new ControlTracker(selectedFieldsLabel, TrackerOperation.Move));

            _trackedControls.Add(new ControlTracker(sortFieldsListView, TrackerOperation.Move | TrackerOperation.Resize));
            _trackedControls.Add(new ControlTracker(sortAvailableFieldsListView, TrackerOperation.Resize));
            _trackedControls.Add(new ControlTracker(sortMoveRightButton, TrackerOperation.Move));
            _trackedControls.Add(new ControlTracker(sortMoveLeftButton, TrackerOperation.Move));
            _trackedControls.Add(new ControlTracker(sortSelectedFieldsLabel, TrackerOperation.Move));
        }

        /// <summary>
        /// Sets the buttons of the toolbar based on the images stored on the fieldschooser image list.
        /// 
        /// </summary>
        private void InitializeButtonImages()
        {
            // Right Buttons
            moveRightButton.ImageList = _fieldChooserImageList;
            moveRightButton.ImageIndex = 0;
            sortMoveRightButton.ImageList = _fieldChooserImageList;
            sortMoveRightButton.ImageIndex = 0;

            // Left Buttons
            moveLeftButton.ImageList = _fieldChooserImageList;
            moveLeftButton.ImageIndex = 9;
            sortMoveLeftButton.ImageList = _fieldChooserImageList;
            sortMoveLeftButton.ImageIndex = 9;

            // up buttons
            moveUpButton.ImageList = _fieldChooserImageList;
            moveUpButton.ImageIndex = 7;
            moveSortUpButton.ImageList = _fieldChooserImageList;
            moveSortUpButton.ImageIndex = 7;

            // down buttons
            moveDownButton.ImageList = _fieldChooserImageList;
            moveDownButton.ImageIndex = 6;
            moveSortDownButton.ImageList = _fieldChooserImageList;
            moveSortDownButton.ImageIndex = 6;

            //ascending
            sortAscendingButton.ImageList = _fieldChooserImageList;
            sortAscendingButton.ImageIndex = 12;
            //descending
            sortDescendingButton.ImageList = _fieldChooserImageList;
            sortDescendingButton.ImageIndex = 13;
        }

        /// <summary>
        /// Loads the images from the TFS Object Model library. 
        /// 
        /// These image lists are stored in TFS OM as resources. These way we keep image fidelity with TE
        /// query editor without having to keep a replica of images.
        /// (hopefully MS won't break the images position on the next version)
        /// </summary>
        private void InitializeFieldChooserImages()
        {
            _fieldChooserImageList = GetFieldChooseIconsImageList();
        }

        #region events
        /// <summary>
        /// Upon the control being shown (first time) initializes the UI widgets with the data
        /// </summary>
        /// <param name="e"></param>
        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);
            FillProjectComboBox(projectComboBox);
            FillProjectComboBox(sortProjectComboBox);
            ResizeColumnsHeaderWidth();

            FillSelectedFields();
            FillSortFields();
        }

        /// <summary>
        ///
        /// 
        /// Resizes the tracked controls based on the resizing of the form.
        /// 
        /// Since we want to resize the pair of list views in equal ways we can't use the anchoring 
        /// algoritm.
        /// 
        /// We only perform the defined operation on the tracked controls <see>InitializeTrackedControls</see> for the definition
        /// of the operations
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ColumnOptionsForm_SizeChanged(object sender, EventArgs e)
        {
            int delta;

            // What is the difference (in points) between the original size and the current one
            // we are going to move/resize the tracked controls based on these change.
            // Half of the pixels go to the right list view and the other half to the left list view
            delta = this.ClientSize.Width - _originalClientSizeWidth;

            this.SuspendLayout();
            _trackedControls.ForEach(f => f.PerformDefinidedOperation(delta / 2));
            this.ResumeLayout();
        }

        /// <summary>
        /// Called when the user presses the OK button.
        /// 
        /// closes the windows, returns that the dialog result is OK and updates the value
        /// for the <see cref="DisplayField"/> and <see cref="SortFields"/> properties
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void okButton_Click(object sender, EventArgs e)
        {
            UpdateDisplayFieldsFromListView();
            UpdateSortFieldsFromListView();

            this.DialogResult = DialogResult.OK;
            this.Close();
        }

        /// <summary>
        /// Event that is called when the user presses the cancel button.
        /// 
        /// Closes the window and signals the the caller that the user has cancelled the
        /// operation
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cancelButton_Click(object sender, EventArgs e)
        {
            this.DialogResult = DialogResult.Cancel;
            this.Close();
        }

        /// <summary>
        /// Event that is called when the user chooses to move the selected fields
        /// to the list of fields to be projected in the query.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void moveFieldsRightButton_Click(object sender, EventArgs e)
        {
            MoveItemsToRight(availableFieldsListView, selectedFieldsListView, ColumnWidth);
        }        

        private void sortMoveRightButton_Click(object sender, EventArgs e)
        {
            MoveItemsToRight(sortAvailableFieldsListView, sortFieldsListView, DefaultSortOrder);
        }
        
        private void moveFieldsLeftButton_Click(object sender, EventArgs e)
        {
            MoveItemsToLeft(selectedFieldsListView, availableFieldsListView);
        }

        private void sortMoveLeftButton_Click(object sender, EventArgs e)
        {
            MoveItemsToLeft(sortFieldsListView, sortAvailableFieldsListView);
        }

        /// <summary>
        /// Event that is called when the user selects the option to move 
        /// up one position the selected (only one) item in one of the fields
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void moveUpButton_Click(object sender, EventArgs e)
        {
            moveSelectedItemUp(selectedFieldsListView);
        }

        private void moveSortUpButton_Click(object sender, EventArgs e)
        {
            moveSelectedItemUp(sortFieldsListView);
        }

        /// <summary>
        /// Event that is called when the user selects the option to move 
        /// down one position the selected (only one) item in the fields tab
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void moveDownButton_Click(object sender, EventArgs e)
        {
            moveSelectedItemDown(selectedFieldsListView);
        }

        private void moveSortDownButton_Click(object sender, EventArgs e)
        {
            moveSelectedItemDown(sortFieldsListView);
        }

        /// <summary>
        /// Event that is called when the user requested a change of the order type
        /// of one or more selected items to be ascending order
        /// 
        /// All selected items in the listview will now be set to have an ascending order
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void sortAscendingButton_Click(object sender, EventArgs e)
        {
            SetSelectedSortAs(sortFieldsListView,AscendingOrder);
        }

        /// <summary>
        /// Event that is called when the user requested a change of the order type
        /// of one or more selected items to be descending order
        /// 
        /// All selected items in the listview will now be set to have an descending order
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void sortDescendingButton_Click(object sender, EventArgs e)
        {
            SetSelectedSortAs(sortFieldsListView,DescendingOrder);
        }

        /// <summary>
        /// Event that is called when one or more items are selected/deselected in the selected
        /// fields list view.
        /// 
        /// Updates the move up/down button states according to the number of items that are selected.
        /// 
        /// up/down enabled if only one item is selected
        /// up/down disabled if no items or more than one item is selected
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void selectedFieldsListView_SelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateMoveUpDownButtonsState(selectedFieldsListView, moveUpButton, moveDownButton);
        }

        /// <summary>
        /// Event that is called when one or more items are selected/deselected in the sort
        /// fields list view.
        /// 
        /// Updates the move up/down button states according to the number of items that are selected.
        /// 
        /// up/down enabled if only one item is selected
        /// up/down disabled if no items or more than one item is selected
        /// 
        /// Updates the ascending/descending sort order button states.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>

        private void sortFieldsListView_SelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateMoveUpDownButtonsState(sortFieldsListView, moveSortUpButton, moveSortDownButton);
            UpdateSortButtonsState(sortFieldsListView);
        }

        /// <summary>
        /// The team project has been changed in the team project filter of the fields tab
        /// 
        /// Either by user action or triggered by keeping the project dropdown in sync.
        /// 
        /// Makes sure the project filter in the sort tab is synched with this one.
        /// Updates the list of work item types acordding to the project that is selected
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void projectComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            SyncProjectComboBox((ComboBox)sender, sortProjectComboBox);
        }

        /// <summary>
        /// The team project has been changed in the team project filter of the sort tab
        /// 
        /// Either by user action or triggered by keeping the project dropdown in sync.
        /// 
        /// Makes sure the project filter in the fields tab is synched with this one.
        /// Updates the list of work item types acordding to the project that is selected
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void sortProjectComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            SyncProjectComboBox((ComboBox)sender, projectComboBox);
        }


        private void workItemTypeComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            ComboBox combo = (ComboBox)sender;

            if (_updatingFields) return;

            _updatingFields = true;
            sortWorkItemTypeComboBox.SelectedIndex = combo.SelectedIndex;

            WorkItemTypeChanged();

            _updatingFields = false;

        }

        private void sortWorkItemTypeComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            ComboBox combo = (ComboBox)sender;

            if (_updatingFields) return;

            _updatingFields = true;
            workItemTypeComboBox.SelectedIndex = combo.SelectedIndex;

            WorkItemTypeChanged();

            _updatingFields = false;
        }

        /// <summary>
        /// Event that is called when the size of the list view that holds the list of available fields.
        /// 
        /// When this event is called we resize the headers of this list view
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void availableFieldsListView_SizeChanged(object sender, EventArgs e)
        {
            ResizeAvailableFieldsListView();
        }

        private void selectedFieldsListView_SizeChanged(object sender, EventArgs e)
        {
            ResizeSelectedFieldsListView();            
        }

        private void sortAvailableFieldsListView_SizeChanged(object sender, EventArgs e)
        {
            ResizeSortAvailableFieldsListView();

        }

        private void sortFieldsListView_SizeChanged(object sender, EventArgs e)
        {
            ResizeSortFieldsListView();
        }
        #endregion

        private void ResizeSelectedFieldsListView()
        {
            int selectedFieldsWidth;

            selectedFieldsWidth = selectedFieldsListView.ClientSize.Width - SystemInformation.VerticalScrollBarWidth;

            selectedFieldsFieldColumn.Width = (int)(selectedFieldsWidth * 0.75);
            selectedFieldsWidthColumn.Width = (int)(selectedFieldsWidth * 0.25);
        }

        private void ResizeSortFieldsListView()
        {
            int sortSelectedFieldsWidth;

            sortSelectedFieldsWidth = sortFieldsListView.ClientSize.Width - SystemInformation.VerticalScrollBarWidth;

            sortFieldHeader.Width = (int)(sortSelectedFieldsWidth * 0.7);
            sortFieldSortHeader.Width = (int)(sortSelectedFieldsWidth * 0.3);
        }

        private void ResizeAvailableFieldsListView()
        {
            availableColumnNameHeader.Width = availableFieldsListView.ClientSize.Width - SystemInformation.VerticalScrollBarWidth;
        }

        private void ResizeSortAvailableFieldsListView()
        {
            sortAvailableColumnNameHeader.Width = sortAvailableFieldsListView.ClientSize.Width - SystemInformation.VerticalScrollBarWidth;            
        }

        /// <summary>
        /// Resizes the columns of the list view (available and selected fields for sort and for project list) based on the combo box size.
        /// 
        /// </summary>
        private void ResizeColumnsHeaderWidth()
        {   
            ResizeAvailableFieldsListView();
            ResizeSelectedFieldsListView();
            ResizeSortAvailableFieldsListView();
            ResizeSortFieldsListView();
        }        

        /// <summary>
        /// Fills the project combo box with the team project
        /// </summary>
        private void FillProjectComboBox(ComboBox combo)
        {
            combo.Items.Clear();

            combo.Items.Add("All Projects");
            foreach (Project project in _workItemStore.Projects)
            {
                combo.Items.Add(project.Name);
            }

            if (String.IsNullOrEmpty(TeamProject))
            {
                combo.SelectedIndex = 0;
            }
            else
            {
                int teamProjectIdx;
                teamProjectIdx = combo.FindStringExact(TeamProject);

                if (teamProjectIdx != -1)
                    combo.SelectedIndex = teamProjectIdx;
            }
        }

        /// <summary>
        /// Fills the UI with the list of fields that have been already selected
        /// </summary>
        private void FillSelectedFields()
        {
            foreach (DisplayField displayField in DisplayFields)
            {
                AddSelectedField(_selectableFields[displayField.FieldName], displayField.Width.ToString(), false);
            }
        }

        /// <summary>
        /// Fills the listview of the fiels already selected for the sort criteria.
        /// </summary>
        private void FillSortFields()
        {
            foreach (Microsoft.TeamFoundation.WorkItemTracking.Client.SortField sortField in SortFields)
            {
                ListViewItem item;

                item = new ListViewItem(sortField.FieldDefinition.Name);
                item.SubItems.Add(GetSortDescription(sortField.SortType));

                item.Tag = sortField.FieldDefinition;

                sortFieldsListView.Items.Add(item);
            }
        }

        /// <summary>
        /// Disables the filter for the work item type.
        /// 
        /// The user will not be able to filter the fields by workitem type (on both tabs)
        /// 
        /// This happens because the user has selected all projects
        /// </summary>
        /// <param name="availableListView"></param>
        /// <param name="workItemTypeCombo"></param>
        private void DisableWorkItemTypeComboBoxes()
        {
            workItemTypeComboBox.Enabled = false;
            sortWorkItemTypeComboBox.Enabled = false;
        }

        /// <summary>
        /// Places into the available fields listview the list of fields
        /// that can be selected
        /// </summary>
        private void UpdateAvailableFieldsUI()
        {
            availableFieldsListView.Items.Clear();
            sortAvailableFieldsListView.Items.Clear();

            foreach (FieldDefinition fieldDefinition in _selectableFields)
            {
                if (DisplayFields.Contains(fieldDefinition.ReferenceName) == false)
                {
                    AddAvailableField(availableFieldsListView, fieldDefinition);
                }

                if (SortFields.Contains(fieldDefinition) == false)
                {
                    AddAvailableField(sortAvailableFieldsListView, fieldDefinition);
                }
            }
        }

        /// <summary>
        /// Fills the work item combo box based on the selected team project 
        /// 
        /// If no team project is selected the combo box is clear and disabled.
        /// 
        /// If a team project is selected then the combo box is filled with the work item
        /// types of the team project
        /// </summary>
        /// <param name="combo"></param>
        /// <param name="projectName"></param>
        private void FillWorkItemTypeComboBox(ComboBox combo, string projectName)
        {
            combo.Items.Clear();

            if (String.IsNullOrEmpty(projectName))
            {
                combo.Enabled = false;
                return;
            }
            combo.Enabled = true;

            combo.Items.Add(AllWorkItemsTypes);
            combo.SelectedIndex = 0;

            if (String.IsNullOrEmpty(projectName) == false)
            {
                Debug.Assert(_workItemStore.Projects[projectName] != null);

                foreach (WorkItemType workItemType in _workItemStore.Projects[projectName].WorkItemTypes)
                {
                    combo.Items.Add(workItemType.Name);
                }
            }
        }

        /// <summary>
        /// Creates the list of fields that can be choseen given the selected project and workitemtype
        /// </summary>
        /// <param name="availableListView"></param>
        /// <param name="workItemTypeCombo"></param>
        private void FillSelectableFieldsList()
        {
            _selectableFields = new FieldDefinitionList(); //resets the list

            // All projects selected?
            if (String.IsNullOrEmpty(SelectedTeamProject))
            {
                foreach (Project teamProject in _workItemStore.Projects)
                {
                    foreach (WorkItemType workItemType in teamProject.WorkItemTypes)
                    {
                        FillSelectableFields(workItemType);
                    }
                }
            }
            else
            {
                bool allWorkItemTypes;

                allWorkItemTypes = String.IsNullOrEmpty(SelectedWorkItemType);

                // If there is no selected work item type then fill the fields for all work item types on this project
                foreach (WorkItemType workItemType in _workItemStore.Projects[SelectedTeamProject].WorkItemTypes)
                {
                    if (allWorkItemTypes || workItemType.Name == SelectedWorkItemType)
                    {
                        FillSelectableFields(workItemType);
                        break;
                    }
                }
            }
        }

        /// <summary>
        /// Fills the list of selectable fields given work item type.
        /// 
        /// If a field is already in the selectable fields then it is simple not added
        /// </summary>
        /// <param name="workItemType"></param>
        private void FillSelectableFields(WorkItemType workItemType)
        {
            foreach (FieldDefinition fieldDefinition in workItemType.FieldDefinitions)
            {
                _selectableFields.Add(fieldDefinition);
            }
        }

        /// <summary>
        /// Adds a field to the list of available fields in a given list view
        /// 
        /// If the field is not part of the selectable fields  list then the field it is not added
        /// to the list view
        /// 
        /// <remarks>Ordered insertion is not guaranteed. The field is inserted at the end of the list. The method <see cref="AddAvailableFieldByOrder"/> guarantees ordered insertion</remarks>
        /// </summary>
        /// <param name="fieldDefinition"></param>
        private void AddAvailableField(ListView listView, FieldDefinition fieldDefinition)
        {
            ListViewItem item;

            // Only add the field if it's on the definiton list
            if (_selectableFields.Contains(fieldDefinition) == false) return;

            item = new ListViewItem(fieldDefinition.Name);
            item.Tag = fieldDefinition;

            item.SubItems.Add(""); // Reserve a sub item for the width or order

            listView.Items.Add(item);
        }

        /// <summary>
        /// Adds an item (representing a field) to the list of available fields in a given list view
        /// 
        /// If the field is not part of the selectable fields list then the field it is not added
        /// to the list view
        /// 
        /// The field is added to the list respecting the alphabetical order of the field name
        /// </summary>
        /// <param name="listView">The list view to which the field is going to be added</param>
        /// <param name="fieldDefinition"></param>
        /// <param name="item"></param>
        private void AddAvailableFieldByOrder(ListView listView, FieldDefinition fieldDefinition, ListViewItem item)
        {
            int itemIndex;

            // Only add the field if it's on the selectable fields definition list
            if (_selectableFields.Contains(fieldDefinition) == false) return;

            for (itemIndex = listView.Items.Count - 1; itemIndex >= 0; itemIndex--)
            {
                FieldDefinition itemFieldDefinition;

                itemFieldDefinition = listView.Items[itemIndex].Tag as FieldDefinition;

                Debug.Assert(itemFieldDefinition != null, "tag doesn't contain the field definition");

                if (String.Compare(fieldDefinition.Name, itemFieldDefinition.Name) >= 0)
                    break;
            }
            listView.Items.Insert(itemIndex + 1, item);
        }

        /// <summary>
        /// Adds a field to the list of selected fields
        /// </summary>
        /// <param name="fieldDefinition"></param>
        private void AddSelectedField(FieldDefinition fieldDefinition, string width, bool selected)
        {
            ListViewItem newItem;

            newItem = new ListViewItem(fieldDefinition.Name);
            newItem.SubItems.Add(width);
            newItem.Tag = fieldDefinition;
            newItem.Selected = selected;

            selectedFieldsListView.Items.Add(newItem);
        }

        /// <summary>
        /// Event that is trigered when the width value is changed.
        /// 
        /// If the width changes and there are items selected on the selected fields
        /// then the width is updated on all selected items
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void maskedWitdhTextBox_TextChanged(object sender, EventArgs e)
        {
            // If there are selected items we must change their width
            if (selectedFieldsListView.SelectedItems.Count > 0)
            {
                string width;

                width = ColumnWidth;

                foreach (ListViewItem item in selectedFieldsListView.SelectedItems)
                {
                    item.SubItems[1].Text = width;
                }
            }
        }

        /// <summary>
        /// Returns the image list for the field chooser icons
        /// 
        /// This imagelist should be disposed when no longer needed
        /// </summary>
        /// <returns></returns>
        private static ImageList GetFieldChooseIconsImageList()
        {
            Bitmap fieldChooserImages;
            ImageList imageList;

            fieldChooserImages = (System.Drawing.Bitmap)ResourceHelper.GetBitmapResource("Microsoft.TeamFoundation.WorkItemTracking.Controls", "Icons.MiscIcons.png");

            imageList = new ImageList() { ImageSize = new Size(16, 16), TransparentColor = Color.Magenta };
            imageList.Images.AddStrip(fieldChooserImages);

            return imageList;
        }


        /// <summary>
        /// Updates the state of the up and down button 
        /// 
        /// The buttons are only enabled if the list view has one and only one selected item
        /// </summary>
        /// <param name="listView"></param>
        /// <param name="moveUpButton"></param>
        /// <param name="moveDownButton"></param>
        private void UpdateMoveUpDownButtonsState(ListView listView, Button moveUpButton, Button moveDownButton)
        {
            moveUpButton.Enabled = moveDownButton.Enabled = listView.SelectedItems.Count == 1;
        }

        /// <summary>
        /// Updates the state of the sort buttons.
        /// 
        /// If there are no selected items then both buttons (ascending/descending order) are enabled
        /// If there is one selected item then the button that will enable to change the order of the selected item
        /// If there is more than one selected item then both buttons will be enabled
        /// </summary>
        /// <param name="sortFieldsListView"></param>
        private void UpdateSortButtonsState(ListView sortFieldsListView)
        {
            if (sortFieldsListView.SelectedItems.Count == 0)
            {
                sortAscendingButton.Enabled = sortDescendingButton.Enabled = false;
            }
            else if (sortFieldsListView.SelectedItems.Count == 1)
            {
                if (sortFieldsListView.SelectedItems[0].SubItems[1].Text == AscendingOrder)
                {
                    sortAscendingButton.Enabled = false;
                    sortDescendingButton.Enabled = true;
                }
                else
                {
                    sortAscendingButton.Enabled = true;
                    sortDescendingButton.Enabled = false;
                }
            }
            else if (sortFieldsListView.SelectedItems.Count > 1)
            {
                sortAscendingButton.Enabled = true;
                sortDescendingButton.Enabled = true;
            }
        }

        /// <summary>
        /// Disables the sort buttons
        /// </summary>
        private void DisableSortButtons()
        {
            sortAscendingButton.Enabled = false;
            sortDescendingButton.Enabled = false;
        }

        /// <summary>
        /// Sets the order of the selected items in a given list view.
        /// </summary>
        /// <param name="sortFieldsListView">the list view that holds the items that contains the sorted fields</param>
        /// <param name="orderType">the type of order to be used. (this is the string that is human readable)</param>
        private void SetSelectedSortAs(ListView sortFieldsListView, string orderType)
        {
            DisableSortButtons();
            if (sortFieldsListView.SelectedItems.Count > 0)
            {
                foreach (ListViewItem item in sortFieldsListView.SelectedItems)
                {
                    item.SubItems[1].Text = orderType;
                }
            }
            UpdateSortButtonsState(sortFieldsListView);
        }
        
        /// <summary>
        /// Move the selected items from the left list view into the right list view and sets the second column
        /// of the item with a value
        /// </summary>
        /// <param name="leftListView">the list view where the selected items will be picked</param>
        /// <param name="rightViewList">the list view where the moved items will be placed</param>
        /// <param name="secondColumnValue">The value to be placed on the moved item</param>
        private void MoveItemsToRight(ListView leftListView, ListView rightViewList, string secondColumnValue)
        {
            leftListView.BeginUpdate();
            rightViewList.BeginUpdate();

            rightViewList.SelectedItems.Clear();

            foreach (ListViewItem item in leftListView.SelectedItems)
            {
                bool focus;

                focus = item.Focused;
                leftListView.Items.Remove(item);

                item.SubItems[1].Text = secondColumnValue;
                rightViewList.Items.Add(item);

                item.Selected = true;
                item.Focused = focus;
            }

            rightViewList.EndUpdate();
            leftListView.EndUpdate();
        }

        /// <summary>
        /// Move the items from the right view list into the left view list.
        /// 
        /// </summary>
        /// <param name="rightViewList"></param>
        /// <param name="leftViewList"></param>
        private void MoveItemsToLeft(ListView rightViewList, ListView leftViewList)
        {
            FieldDefinition fieldDefinition;

            rightViewList.BeginUpdate();
            leftViewList.BeginUpdate();

            foreach (ListViewItem item in rightViewList.SelectedItems)
            {
                rightViewList.Items.Remove(item);

                fieldDefinition = item.Tag as FieldDefinition;

                Debug.Assert(fieldDefinition != null);

                AddAvailableFieldByOrder(leftViewList, fieldDefinition, item);
            }

            leftViewList.EndUpdate();
            rightViewList.EndUpdate();
        }

        /// <summary>
        /// Moves the selected item (<strong>ONLY ONE</strong> item) one position up in the list view
        /// 
        /// </summary>
        /// <param name="listView"></param>
        private static void moveSelectedItemDown(ListView listView)
        {
            int selectedItemIndex;

            Debug.Assert(listView.SelectedItems.Count == 1);

            selectedItemIndex = listView.SelectedIndices[0];

            if (selectedItemIndex < listView.Items.Count - 1)
            {
                ListViewItem item;

                listView.BeginUpdate();

                item = listView.Items[selectedItemIndex];

                listView.Items.RemoveAt(selectedItemIndex);
                listView.Items.Insert(selectedItemIndex + 1, item);

                listView.EndUpdate();
            }
        }

        /// <summary>
        /// Moves the selected item (<strong>ONLY ONE item</strong>) one position down in the list view
        /// </summary>
        /// <param name="listView"></param>
        private static void moveSelectedItemUp(ListView listView)
        {
            int selectedItemIndex;

            Debug.Assert(listView.SelectedItems.Count == 1);
        
            selectedItemIndex = listView.SelectedIndices[0];

            if (selectedItemIndex > 0)
            {
                ListViewItem item;

                listView.BeginUpdate();

                item = listView.Items[selectedItemIndex];

                listView.Items.RemoveAt(selectedItemIndex);
                listView.Items.Insert(selectedItemIndex - 1, item);

                listView.EndUpdate();
            }
        }


        /// <summary>
        /// Reacts to a change in one of the team project combo box filter and makes sure the other
        /// filter is in sync with this change.
        /// 
        /// Calls the <seealso cref="ProjectChanged"/> to make sure we act accordingly to to the new filter
        /// value
        /// </summary>
        /// <param name="changedComboBox">The project combo box that has just been changed</param>
        /// <param name="toSyncComboBox">The project combo box that we need to guarantee it's in sync</param>
        private void SyncProjectComboBox(ComboBox changedComboBox, ComboBox toSyncComboBox)
        {
            //If we are in the middle of updating the fields. Skip this action. This happens because when we sync the field we also trigger a change
            // in the sync combo box which means the event will be retriggered. This makes sure we only work once per sync operation
            if (_updatingFields) return;

            //Signals we are in the middle of a synch operation
            _updatingFields = true;

            //makes sure the other combobox is synched
            toSyncComboBox.SelectedItem = changedComboBox.SelectedItem;

            ProjectChanged(); // react to the change of the project

            _updatingFields = false; // and we are done.
        }


        /// <summary>
        /// The selected project has changed
        /// 
        /// If the all projects has been chosen disables the work item type filter otherwise update the values of the work item type 
        /// filters (both fields and sort tabs)
        /// 
        /// Updates the work item type combo boxes.
        /// 
        /// Updates the list of fields that are selectable (according to the filter selection and the fields that are already selected)
        /// and updates the UI accordingly
        /// </summary>
        /// <param name="availableListView"></param>
        /// <param name="projectCombo"></param>
        /// <param name="workItemTypeCombo"></param>
        private void ProjectChanged()
        {
            if (IsTeamProjectSelected == false) // all projects?
            {
                DisableWorkItemTypeComboBoxes();
            }
            else
            {
                workItemTypeComboBox.Enabled = true;
                sortWorkItemTypeComboBox.Enabled = true;

                FillWorkItemTypeComboBox(workItemTypeComboBox, SelectedTeamProject);
                FillWorkItemTypeComboBox(sortWorkItemTypeComboBox, SelectedTeamProject);
            }

            // Updates the UI according to the filter settings
            FillSelectableFieldsList();
            UpdateAvailableFieldsUI();
        }

        /// <summary>
        /// The work item type has changed in the filter.
        /// 
        /// Updates the list of fields that are selectable
        /// Updates the UI (selecteable fields combo boxes in both tabs) according to this new list
        /// </summary>
        private void WorkItemTypeChanged()
        {
            FillSelectableFieldsList();
            UpdateAvailableFieldsUI();
        }

        /// <summary>
        /// Updates the property <see cref="SortFields"/> with the fields that are currently 
        /// set on the sort fields list view.
        /// 
        /// <remarks><see cref="SortFields"/> cannot be null</remarks>
        /// </summary>
        private void UpdateSortFieldsFromListView()
        {
            Debug.Assert(SortFields != null, "Sort fields cannot be null");

            SortFields.Clear();
            foreach (ListViewItem item in sortFieldsListView.Items)
            {
                Microsoft.TeamFoundation.WorkItemTracking.Client.SortField sortField;
                FieldDefinition field;

                field = item.Tag as FieldDefinition;

                Debug.Assert(field != null, "tag value is not set with a FieldDefinition");

                sortField = new Microsoft.TeamFoundation.WorkItemTracking.Client.SortField(field, GetSortType(item.SubItems[1].Text));
                SortFields.Add(sortField);
            }
        }

        /// <summary>
        /// Updates the property <see cref="DisplayFields"/> with the fields that are currently 
        /// set on the selected fields list view
        /// </summary>
        private void UpdateDisplayFieldsFromListView()
        {
            if (DisplayFields == null) DisplayFields = new DisplayFieldCollection();
            DisplayFields.Clear();
            foreach (ListViewItem item in selectedFieldsListView.Items)
            {
                DisplayField displayField;
                FieldDefinition field;

                field = item.Tag as FieldDefinition;

                Debug.Assert(field != null, "tag value is not set with a FieldDefinition");

                displayField = new DisplayField(field.ReferenceName, Convert.ToInt32(item.SubItems[1].Text)); //referencename,width
                DisplayFields.Add(displayField);
            }
        }

        /// <summary>
        /// Transforms a sort according to the UI value (textual) into an enumerated value
        /// <see cref="Microsoft.TeamFoundation.WorkItemTracking.Client.SortType"/> that the OM understands
        /// </summary>
        /// <param name="sortType"></param>
        /// <returns></returns>
        private SortType GetSortType(string sortType)
        {
            switch (sortType)
            {
                case AscendingOrder:
                    return SortType.Ascending;
                case DescendingOrder:
                    return SortType.Descending;

                default:
                    throw new Exception("unknown sort type");
            }
        }

        /// <summary>
        /// Gets the description (textual description) used that is shown to the user at the UI level given a sort
        /// type as defined by the enumeration of type <see cref="Microsoft.TeamFoundation.WorkItemTracking.Client.SortType"/>
        /// </summary>
        /// <param name="sortType"></param>
        /// <returns></returns>
        private string GetSortDescription(SortType sortType)
        {
            switch (sortType)
            {
                case SortType.Ascending:
                    return AscendingOrder;
                case SortType.Descending:
                    return DescendingOrder;
                default:
                    throw new Exception("unknown sort type");
            }
        }
    }
}